using System.Collections;
using System.Globalization;
using System.IO;
using System.Xml;
using EnvDTE;
using Microsoft.Practices.RecipeFramework;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel;
using Microsoft.Practices.RepositoryFactory.CommonHelpers;
using Microsoft.Practices.RepositoryFactory.Properties;
using Config = System.Configuration.Configuration;

namespace Microsoft.Practices.RepositoryFactory.Actions.Configuration
{
    public class SetRepositoryMappingAction : ConfigurableAction
    {
        private Config configuration;
        ArrayList entities;
        private string dataAccessClassNamespace;
        private string businessEntityClassNamespace;
        private Project dataAccessProject;

        /// <summary>
        /// Gets or sets the configuration.
        /// </summary>
        /// <value>The configuration.</value>
        [Input(Required = true)]
        public Config Configuration
        {
            get { return configuration; }
            set { configuration = value; }
        }

        /// <summary>
        /// Gets or sets the entites to generate.
        /// </summary>
        /// <value>Entities.</value>
        [Input(Required = true)]
        public ArrayList Entities
        {
            get { return entities; }
            set { entities = value; }
        }

        /// <summary>
        /// Gets or sets the Data Access Class Namespace.
        /// </summary>
        /// <value>Data Access Class Namespace</value>
        [Input(Required = true)]
        public string DataAccessClassNamespace
        {
            get { return dataAccessClassNamespace; }
            set { dataAccessClassNamespace = value; }
        }

        /// <summary>
        /// Gets or sets the Data Access Project.
        /// </summary>
        /// <value> The Data Access Project </value>
        [Input(Required = true)]
        public Project DataAccessProject
        {
            get { return dataAccessProject; }
            set { dataAccessProject = value; }
        }

        /// <summary>
        /// Gets or sets the Business Entities project namespace.
        /// </summary>
        /// <value>Business entities namespace.</value>
        [Input(Required = true)]
        public string BusinessEntityClassNamespace
        {
            get { return businessEntityClassNamespace; }
            set { businessEntityClassNamespace = value; }
        }

        public override void Execute()
        {
            /* We're using XML in this method because there's a 
             * dll issue with Guidance Package and the target project. They're referencing 
             * the same assembly but in different locations and that generates
             * runtime errors.
             */
            NormalizeConfiguration(configuration.FilePath);
            XmlDocument document = new XmlDocument();
            document.Load(configuration.FilePath);
            EnsureConfigurationSectionDeclarationExists("repositoryFactory", Resources.RepositoryFactoryConfigSectionType, document);
            XmlNode repositoryFactorySection = GetOrCreateConfigurationSection("repositoryFactory", document);
            AddRepositoryMappings(repositoryFactorySection, document);
            document.Save(configuration.FilePath);
        }



        private void AddRepositoryMappings(XmlNode repositoryFactorySection, XmlDocument document)
        {
            XmlNode repositoriesNode = repositoryFactorySection.FirstChild;

            string assemblyName = dataAccessProject.Properties.Item("AssemblyName").Value.ToString();

            foreach (Entity entity in this.entities)
            {
                string entityRepositoryName = GetRepositoryTypeName(entity.Name, assemblyName);
                string interfaceRepositoryName = GetInterfaceTypeName(entity.Name, assemblyName);


                XmlNode element = repositoriesNode.SelectSingleNode("//add[@interfaceType='" + interfaceRepositoryName + "']");

                if (element == null)
                {
                    element = document.CreateElement("add");

                    XmlAttribute repositoryInterfaceAttribute = document.CreateAttribute("interfaceType");
                    repositoryInterfaceAttribute.Value = interfaceRepositoryName;
                    element.Attributes.Append(repositoryInterfaceAttribute);

                    XmlAttribute repositoryTypeAttribute = document.CreateAttribute("repositoryType");
                    repositoryTypeAttribute.Value = entityRepositoryName;
                    element.Attributes.Append(repositoryTypeAttribute);

                    repositoriesNode.AppendChild(element);
                }
                else
                {
                    XmlAttribute repositoryTypeAttribute = element.Attributes["repositoryType"];
                    repositoryTypeAttribute.Value = entityRepositoryName;
                }
            }
        }

        private XmlNode GetOrCreateConfigurationSection(string sectionName, XmlDocument configFile)
        {
            XmlNode repositoryFactorySection = configFile.SelectSingleNode("//configuration/repositoryFactory");
            if (repositoryFactorySection == null)
            {
                repositoryFactorySection = configFile.CreateElement(sectionName);
                XmlNode repositoriesNode = configFile.CreateElement("repositories");
                repositoryFactorySection.AppendChild(repositoriesNode);

                XmlNode configurationNode = configFile.SelectSingleNode("//configuration");
                configurationNode.AppendChild(repositoryFactorySection);
            }
            return repositoryFactorySection;
        }

        private void EnsureConfigurationSectionDeclarationExists(string sectionName, string sectionType, XmlDocument configFile)
        {
            XmlNode configSectionNode = configFile.SelectSingleNode("//configuration/configSections");

            if (configSectionNode == null)
            {
                configSectionNode = configFile.CreateElement("configSections");
                XmlNode configurationNode = configFile.SelectSingleNode("//configuration");

                if (configurationNode.FirstChild != null)
                {
                    configurationNode.InsertBefore(configSectionNode, configurationNode.FirstChild);
                }
                else
                {
                    configurationNode.AppendChild(configSectionNode);
                }
            }

            XmlNode repositoryFactorySection = configSectionNode.SelectSingleNode("//section[@name='" + sectionName + "']");

            if (repositoryFactorySection == null)
            {
                repositoryFactorySection = configFile.CreateElement("section");

                XmlAttribute nameAttribute = configFile.CreateAttribute("name");
                nameAttribute.Value = sectionName;
                repositoryFactorySection.Attributes.Append(nameAttribute);


                XmlAttribute typeAttribute = configFile.CreateAttribute("type");
                typeAttribute.Value = sectionType;
                repositoryFactorySection.Attributes.Append(typeAttribute);

                configSectionNode.AppendChild(repositoryFactorySection);
            }
        }



        public override void Undo()
        {
            //TODO: Not Implemented
        }

        private string GetInterfaceTypeName(string entityName, string assemblyName)
        {
            return string.Format(CultureInfo.CurrentCulture, "{1}.Interface.I{0}Repository, {2}", entityName, dataAccessClassNamespace, assemblyName);
        }

        private string GetRepositoryTypeName(string entityName, string assemblyName)
        {
            string repositoryNamespace = NameResolver.ResolveNamespace(false, dataAccessClassNamespace, entityName);
            return string.Format(CultureInfo.CurrentCulture, "{1}.{0}Repository, {2}", entityName, repositoryNamespace, assemblyName);
        }

        private void NormalizeConfiguration(string filePath)
        {
            string content = File.ReadAllText(filePath);
            content = content.Replace("xmlns=\"http://schemas.microsoft.com/.NetConfiguration/v2.0\"", string.Empty);
            File.WriteAllText(filePath, content);
        }
    }
}
